home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / scheme / schematk / src_1152.lha / Doc.subproj / DocText.m < prev    next >
Encoding:
Text File  |  1991-12-15  |  33.6 KB  |  1,116 lines

  1. /* Copyright รก 1991 Gustavus Adolphus College.  All rights reserved.
  2.  *
  3.  * Schematik was developed by Gustavus Adolphus College (GAC) with
  4.  * support from NeXT Computer, Inc.  Permission to copy this software,
  5.  * to redistribute it, and to use it for any purpose is granted,
  6.  * subject to the following restrictions and understandings.
  7.  *
  8.  * 1. Any copy made of this software must include this copyright
  9.  * notice in full.
  10.  *
  11.  * 2. Users of this software agree to make their best efforts (a) to
  12.  * return to the GAC Mathematics and Computer Science Department any
  13.  * improvements or extensions that they make, so that these may be
  14.  * included in future releases; and (b) to inform GAC of noteworthy
  15.  * uses of this software.
  16.  *
  17.  * 3. All materials developed as a consequence of the use of this
  18.  * software shall duly acknowledge such use, in accordance with the
  19.  * usual standards of acknowledging credit in academic research.
  20.  *
  21.  * 4. GAC makes no express or implied warranty or representation of
  22.  * any kind with respect to this software, including any warranty
  23.  * that the operation of this software will be error-free.  ANY
  24.  * IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR
  25.  * PURPOSE IS HEREBY DISCLAIMED.  GAC is under no obligation to
  26.  * provide any services, by way of maintenance, update, or otherwise.
  27.  *
  28.  * 5. In conjunction with products arising from the use of this
  29.  * material, there shall be no use of the name of Gustavus Adolphus
  30.  * College nor of any adaptation thereof in any advertising,
  31.  * promotional, or sales literature without prior written consent
  32.  * from GAC in each case.
  33.  */
  34.  
  35. #import "DocText.h"
  36. #import "../defines.h"
  37. #import DocWin_h
  38. #import FindAgent_h
  39. #import InteractionWin_h
  40. #import PrefAgent_h
  41. #import <appkit/Application.h>
  42. #import <appkit/Font.h>
  43. #import <appkit/Matrix.h>
  44. #import <appkit/Menu.h>
  45. #import <appkit/NXCursor.h>
  46. #import <appkit/NXImage.h>
  47. #import <appkit/ScrollView.h>
  48. #import <dpsclient/event.h>
  49. #import <sys/types.h>
  50. #import <nextdev/keycodes.h>
  51. #import <objc/List.h>
  52. #import <NXCType.h>
  53. #import <string.h>
  54. #import <streams/streams.h>
  55. #import <ctype.h>
  56. extern void usleep(unsigned);
  57. extern void NXBeep(void);
  58. extern void *malloc(), *realloc(), free();
  59.  
  60. #define LEFT_PAREN        '('
  61. #define RIGHT_PAREN        ')'
  62. #define LEFT_BRACKET        '['
  63. #define RIGHT_BRACKET        ']'
  64. #define LEFT_BRACE        '{'
  65. #define RIGHT_BRACE        '}'
  66. #define LEFT_PARENS        "([{"
  67. #define RIGHT_PARENS        ")]}"
  68. #define PBLINKDELAY        175000
  69. #define COMMENTSTRING        ";"
  70. #define COMMENTCHAR        ';'
  71. #define BADLINEBEGINSET        " \n\t\r\v\f)]};"
  72. #define DELIMETERSET        " \n\t\r\v\f();\"\'`|[]{}\377"
  73. #define PREFIXCHARS        "\'`,@#"
  74. #define BIG_INDENT        4
  75. #define SMALL_INDENT        2
  76. #define MAXTOK            1024
  77. #define MAX_FWD_ONLY        4
  78. #define TABWIDTH        8
  79. #define PAREN_NO_MATCH        -1
  80. #define NOT_REALLY_PAREN    -2
  81.  
  82. #define larrow    172
  83. #define uarrow    173
  84. #define rarrow    174
  85. #define darrow    175
  86.  
  87. typedef struct cntxt {
  88.   struct cntxt *next;
  89.   int elts;
  90.   int distinguishedElts;
  91.   int startPosition;
  92.   int alignmentPosition;
  93.   BOOL isLet;
  94. } Context;
  95.  
  96. static BOOL inSet(const char, const char *);
  97. static int findParensMatch(id, const char, int, char *);
  98. static int findBeginningOfScope(id, int, char *);
  99. static int skipspacecommentnewline(const char[], int, int);
  100. static int skipstring(const char[], int, int);
  101. static int findEndOfScope(id, int, char *);
  102. static int getcolumnofposition(id, int);
  103. static void newElt(Context *, BOOL, NXStream *);
  104.  
  105. static char *selectionBuffer=NULL;
  106.  
  107. BOOL inOtherMethod=NO;
  108. int last1ClickPos=0;
  109.  
  110. @implementation DocText
  111.  
  112. #define PRE_CHANGE    NXSelPt start,end; int tlength=0,i; BOOL wasInOtherMethod=inOtherMethod;    \
  113.             if (!wasInOtherMethod)                                \
  114.               { inOtherMethod=YES; [super getSel:&start :&end]; tlength=textLength;    }
  115.  
  116. #define POST_CHANGE(POSITION,FROM,TO)    \
  117.   if (!wasInOtherMethod)        \
  118.     { inOtherMethod=NO;            \
  119.       for(i=[markerList count]-1; i; i--) \
  120.         [[markerList objectAt:i] adjustTo:(POSITION) forPositionsFrom:(FROM) to:(TO) andAfterBy:(textLength-tlength)];    \
  121.       if ([window isMemberOf:[InteractionWin class]])                                    \
  122.         [[markerList objectAt:0] adjustTo:(POSITION) forPositionsFrom:(FROM) to:(TO) andAfterBy:(textLength-tlength)];    \
  123.       [indentationState changeAt:FROM];}
  124.  
  125. - initFrame:(NXRect *)r
  126. {
  127.     return [self initFrame:r text:"" alignment:NX_LEFTALIGNED];
  128. }
  129.  
  130. - initFrame:(NXRect *)r text:(const char *)text alignment:(int)mode
  131. {
  132.     char *font=[[PrefAgent new] interFont];
  133.  
  134.     [super initFrame:r text:text alignment:mode];
  135.     markerList=[[List alloc] initCount:1];
  136.     [markerList addObject:[[Marker alloc] initAt:0 type:MARKRIGHT]];
  137.     if (!(NXOrderStrings((unsigned char *)font, (unsigned char *)GOODFONT1, YES, strlen(GOODFONT1), NULL)&&
  138.            NXOrderStrings((unsigned char *)font, (unsigned char *)GOODFONT2, YES, strlen(GOODFONT2), NULL)))
  139.       [self setFont:[Font newFont:font size:[[PrefAgent new] interFontSize]]];
  140.     else
  141.       [self setFont:[Font newFont:GOODFONT1 size:11]];
  142.     [self setSel:0 :0];
  143.     indentationState = [[IndentationState alloc] initText:self];
  144.     return self;
  145. }
  146.  
  147. - free
  148. {
  149.   [[markerList freeObjects] free];
  150.   [indentationState free];
  151.   return [super free];
  152. }
  153.  
  154. - keyDown:(NXEvent *)theEvent
  155. {
  156.     switch (theEvent->data.key.charCode)
  157.       {
  158.         case soh: if (theEvent->flags&NX_CONTROLMASK)    // move to beginning of line
  159.                 {
  160.                       NXSelPt start,end; int pos;
  161.                   [super getSel:&start :&end];
  162.               pos=[super positionFromLine:[super lineFromPosition:start.cp]];
  163.               return [self setSel:pos :pos];
  164.             }
  165.           break;
  166.     case stx: if (theEvent->flags&NX_CONTROLMASK)    // move backward one character
  167.                 {
  168.                       theEvent->flags=NX_NUMERICPADMASK;
  169.               theEvent->data.key.charSet=1;
  170.               theEvent->data.key.charCode=larrow;
  171.             }
  172.           break;
  173.         case etx: if (theEvent->flags&(NX_COMMANDMASK|NX_NUMERICPADMASK))    // Enter or Cntl-c
  174.                    return [[[[[[NXApp mainMenu] findCellWithTag:MENU_ACTIONS] target]
  175.                             itemList] selectCellWithTag:MENU_ACTIONS_Evaluate] sendAction];
  176.           else if (theEvent->flags&NX_CONTROLMASK)
  177.             theEvent->data.key.charCode=nul;
  178.                   break;
  179.     case eot: if (theEvent->flags&NX_CONTROLMASK)    // delete next character
  180.                 {
  181.                       NXSelPt start,end;
  182.                   [super getSel:&start :&end];
  183.               [self setSel:start.cp+1 :start.cp+1];    // move cursor forward 1 char.
  184.               theEvent->data.key.charCode=bs;        // Text does a backspace
  185.             }
  186.           break;
  187.     case enq: if (theEvent->flags&NX_CONTROLMASK)    // move to end of line
  188.                 {
  189.                       NXSelPt start,end; int pos,line;
  190.                   [super getSel:&start :&end];
  191.                       line=[super lineFromPosition:start.cp];
  192.               pos=([super positionFromLine:line+1]<0)?textLength:[super positionFromLine:line+1]-1;
  193.               return [self setSel:pos :pos];
  194.             }
  195.           break;
  196.     case ack: if (theEvent->flags&NX_CONTROLMASK)    // move forward one character
  197.                 {
  198.                       theEvent->flags=NX_NUMERICPADMASK;
  199.               theEvent->data.key.charSet=1;
  200.               theEvent->data.key.charCode=rarrow;
  201.             }
  202.           break;
  203.     case bel: if (theEvent->flags&NX_CONTROLMASK)    // bell
  204.                 {
  205.                       NXBeep(); return self;
  206.             }
  207.           break;
  208.     case bs : break;    // delete previous character (implemented by Text)
  209.         case ht : return [self formatTextSelectionOnly:YES];    // indent line
  210.         case nl : break;    // newline (non-indenting cr; implemented by Text)
  211.     case vt : if (theEvent->flags&NX_CONTROLMASK)    // delete forward to end of line
  212.                 {
  213.                       NXSelPt start,end; int pos,line;
  214.                   [super getSel:&start :&end];
  215.                       line=[super lineFromPosition:start.cp];
  216.               pos=([super positionFromLine:line+1]<0)?textLength:[super positionFromLine:line+1]-1;
  217.               [window disableFlushWindow];
  218.               [self setSel:start.cp :pos];
  219.               [window reenableFlushWindow];
  220.               return [self cut:self];
  221.             }
  222.           break;
  223.         case cr : {PRE_CHANGE        // carriage return
  224.               [window disableFlushWindow];
  225.                   [super keyDown:theEvent];
  226.           [window reenableFlushWindow];
  227.                   POST_CHANGE(start.cp+1,start.cp,end.cp)}
  228.                   if (([[PrefAgent new] autoindent]&&!(theEvent->flags&NX_ALTERNATEMASK))||
  229.                        ((theEvent->flags&NX_ALTERNATEMASK)&&![[PrefAgent new] autoindent]))
  230.                     [self formatTextSelectionOnly:YES];
  231.           [window flushWindow];
  232.                   return self;
  233.     case so : if (theEvent->flags&NX_CONTROLMASK)    // move down one line
  234.                 {
  235.                       theEvent->flags=NX_NUMERICPADMASK;
  236.               theEvent->data.key.charSet=1;
  237.               theEvent->data.key.charCode=darrow;
  238.             }
  239.           break;
  240.     case dle: if (theEvent->flags&NX_CONTROLMASK)    // move up one line
  241.                 {
  242.                       theEvent->flags=NX_NUMERICPADMASK;
  243.               theEvent->data.key.charSet=1;
  244.               theEvent->data.key.charCode=uarrow;
  245.             }
  246.           break;
  247.     case em : if (theEvent->flags&NX_CONTROLMASK)    // indent line (shift-tab)
  248.                 {
  249.               return [self paste:self];
  250.             }
  251.           else
  252.                 return [self formatTextSelectionOnly:YES];
  253.  
  254.         case RIGHT_PAREN  :
  255.         case RIGHT_BRACKET:
  256.         case RIGHT_BRACE  :
  257.               {PRE_CHANGE
  258.           [super keyDown:theEvent];
  259.           POST_CHANGE(start.cp,start.cp,end.cp)
  260.           {char c;
  261.           [super getSel:&start :&end];
  262.           [super getSubstring:&c start:end.cp-1 length:1];
  263.           if (inSet(c,RIGHT_PARENS)&&[[PrefAgent new] matchParens])
  264.             {
  265.               int pos;
  266.               char buffer[textLength+1];
  267.               [super getSubstring:buffer start:0 length:textLength];
  268.               if ((pos = findParensMatch(self,(char)theEvent->data.key.charCode,start.cp-1,buffer))==PAREN_NO_MATCH)
  269.             NXBeep();
  270.               else if (pos >= 0)
  271.             {
  272.               [self setSel:pos :pos+1];
  273.               NXPing();
  274.               usleep(PBLINKDELAY);
  275.               [self setSel:start.cp :start.cp];
  276.             }
  277.             }}}
  278.           return self;
  279.         }
  280.     {PRE_CHANGE
  281.     [super keyDown:theEvent];
  282.     POST_CHANGE(start.cp,start.cp,end.cp)}
  283.     return self;
  284. }
  285.  
  286. - mouseDown:(NXEvent*)theEvent
  287. {
  288.     NXSelPt start,end;
  289.  
  290.     switch (theEvent->data.mouse.click)
  291.       {
  292.         case 1 : [super mouseDown:theEvent];
  293.                  [self getSel:&start :&end];
  294.                  last1ClickPos = start.cp;
  295.          return self;
  296.         case 2 : [window disableFlushWindow];
  297.              [super mouseDown:theEvent];
  298.              {
  299.            char c[4];
  300.            [super getSel:&start :&end];
  301.            [super getSubstring:c start:start.cp-2 length:3];
  302.                    if (inSet(c[2],LEFT_PARENS RIGHT_PARENS)&&!((c[0]=='#')&&(c[1]=='\\')))
  303.              {
  304.                [window reenableFlushWindow];
  305.                if (([[PrefAgent new] dblClickMatch])&&((end.cp-start.cp)==1))
  306.              {
  307.                char buffer[textLength+1];
  308.                int temp;
  309.                [super getSubstring:buffer start:0 length:textLength];
  310.                if ((temp = findParensMatch(self, buffer[start.cp], start.cp, buffer))<0)
  311.                  {
  312.                    [window flushWindow];
  313.                    return self;
  314.                  }
  315.                if (inSet(buffer[start.cp], LEFT_PARENS))
  316.                    [super setSel:start.cp :temp+1];
  317.                else
  318.                   [super setSel:temp :start.cp+1];
  319.              }
  320.                return self;
  321.              }
  322.            else if (inSet(c[2], DELIMETERSET)&&!((c[0]=='#')&&(c[1]=='\\')))
  323.              {
  324.                [[window reenableFlushWindow] flushWindow];
  325.                return self;
  326.              }
  327.                    else
  328.              {
  329.                int i,j;
  330.                char buffer[textLength+1];
  331.                [super getSubstring:buffer start:0 length:textLength];
  332.                for(i=start.cp; (i>=0)&&(!inSet(buffer[i],DELIMETERSET)||((i>=2)&&(buffer[i-2]=='#')&&(buffer[i-1]=='\\'))); i--);
  333.                        for(j=end.cp; (j<textLength)&&(!inSet(buffer[j],DELIMETERSET)||((j>=2)&&(buffer[j-2]=='#')&&(buffer[j-1]=='\\'))); j++);
  334.                        [super setSel:i+1 :j];
  335.                [[window reenableFlushWindow] flushWindow];
  336.              }
  337.          }
  338.          return self;
  339.         case 3 : break;
  340.         default: [self selectScope2];
  341.              return self;
  342.       }
  343.     return [super mouseDown:theEvent];
  344. }
  345.  
  346. - paste:sender
  347. {
  348.     PRE_CHANGE
  349.     [super paste:sender];
  350.     POST_CHANGE(end.cp+(textLength-tlength),start.cp,end.cp)
  351.     if ([[PrefAgent new] formatAfterPaste])
  352.       {
  353.         [markerList addObject:[[Marker alloc] initAt:(end.cp+(textLength-tlength)) type:MARKCENTER]];
  354.         [self setSel:start.cp :(end.cp+(textLength-tlength))];
  355.         [self formatTextSelectionOnly:YES];
  356.         end.cp = [[markerList objectAt:([markerList count]-1)] markerPosition];
  357.         [self setSel:end.cp :end.cp];
  358.         [[markerList removeLastObject] free];
  359.       }
  360.     return self;
  361. }
  362.  
  363. - clear:sender
  364. {
  365.     PRE_CHANGE
  366.     [super clear:sender];
  367.     POST_CHANGE(start.cp,start.cp,end.cp)
  368.     return self;
  369. }
  370.  
  371. - delete:sender
  372. {
  373.     PRE_CHANGE
  374.     [super delete:sender];
  375.     POST_CHANGE(start.cp,start.cp,end.cp)
  376.     return self;
  377. }
  378.  
  379. - setText:(const char *)aString
  380. {
  381.     PRE_CHANGE
  382.     [super setText:aString];
  383.     POST_CHANGE(0,0,tlength)
  384.     return self;
  385. }
  386.  
  387. - readText:(NXStream *)stream
  388. {
  389.     PRE_CHANGE
  390.     [super readText:stream];
  391.     {
  392.       NXStream *stream = [self stream];
  393.       int c;
  394.       while((c = NXGetc(stream)) != EOF)
  395.         if(c == '\t'){
  396.           int pos = NXTell(stream);
  397.           int spaces = TABWIDTH - getcolumnofposition(self, pos-1) % TABWIDTH;
  398.           char spacestring[spaces+1];
  399.           [self setSel:pos-1 :pos];
  400.           memset(spacestring, ' ', spaces);
  401.           spacestring[spaces] = '\0';
  402.           [self replaceSel:spacestring];
  403.           stream = [self stream];
  404.           NXSeek(stream, pos, NX_FROMSTART);
  405.         }
  406.       [self setSel:0 :0];
  407.     }
  408.     POST_CHANGE(0,0,tlength)
  409.     return self;
  410. }
  411.  
  412. - replaceSel:(const char *)aString length:(int)length
  413. {
  414.     PRE_CHANGE
  415.     [super replaceSel:aString length:length];
  416.     POST_CHANGE(end.cp+(textLength-tlength),start.cp,end.cp)
  417.     return self;
  418. }
  419.  
  420. - readSelectionFromPasteboard:pboard
  421. {
  422.     id ret;
  423.     PRE_CHANGE
  424.     ret = [super readSelectionFromPasteboard:pboard];
  425.     POST_CHANGE(end.cp+(textLength-tlength),start.cp,end.cp)
  426.     return ret;
  427. }
  428.  
  429. - setFont:aFont
  430. {
  431.     [window disableFlushWindow];
  432.     [super setFont:aFont];
  433.     [[[window display] reenableFlushWindow] flushWindow];
  434.     [[window scrollView] setLineScroll:[super lineHeight]];
  435.     [[window scrollView] setPageScroll:[super lineHeight]];
  436.     return self;
  437. }
  438.  
  439.  
  440. - appendNewlineIfNeeded
  441. {
  442.     NXSelPt start,end;
  443.     int textPointer = [[markerList objectAt:0] markerPosition];
  444.  
  445.     if ([[PrefAgent new] transcriptMode])
  446.       [[markerList objectAt:0] adjustTo:textLength-1];
  447.     [super getSel:&start :&end];
  448.     [markerList addObject:[[Marker alloc] initAt:end.cp type:MARKCENTER]];
  449.     [markerList addObject:[[Marker alloc] initAt:start.cp type:MARKCENTER]];
  450.     if (textPointer>0)
  451.       {
  452.         char buffer[2];
  453.         [super getSubstring:buffer start:textPointer-1 length:1];
  454.         if (buffer[0]!=NEWLINECHAR)
  455.           {
  456.             [[self setSel:textPointer :textPointer] replaceSel:NEWLINESTRING];
  457.             [window setDocEdited:YES];
  458.             [self setInsertionBar:textPointer+1];
  459.           }
  460.       }
  461.     start.cp = [[markerList objectAt:([markerList count]-1)] markerPosition];
  462.     end.cp = [[markerList objectAt:([markerList count]-2)] markerPosition];
  463.     [self setSel:start.cp :end.cp];
  464.     [[markerList removeLastObject] free];
  465.     [[markerList removeLastObject] free];
  466.     return self;
  467. }
  468.  
  469. - appendText:(const char *)aString commented:(BOOL)aBoolean withNewline:(BOOL)aBoolean2
  470. {
  471.     NXSelPt start,end;
  472.     int textPointer = [[markerList objectAt:0] markerPosition],i;
  473.  
  474.     if ([[PrefAgent new] transcriptMode])
  475.       [[markerList objectAt:0] adjustTo:textLength-1];
  476.     [super getSel:&start :&end];
  477.     [markerList addObject:[[Marker alloc] initAt:end.cp type:MARKCENTER]];
  478.     [markerList addObject:[[Marker alloc] initAt:start.cp type:MARKCENTER]];
  479.     [window disableFlushWindow];
  480.     if (aBoolean)
  481.       {
  482.         [super setSel:textPointer :textPointer];
  483.         if ([super positionFromLine:[super lineFromPosition:textPointer]]==textPointer)
  484.           [self replaceSel:COMMENTSTRING];
  485.         for(i=0; aString[i]; i++)
  486.           {
  487.             [self replaceSel:(aString+i) length:1];
  488.             if ((aString[i]==NEWLINECHAR)&&(aString[i+1])&&(aString[i+1]!=NEWLINECHAR)&&(aString[i+1]!=COMMENTCHAR))
  489.               [self replaceSel:COMMENTSTRING];
  490.           }
  491.       }
  492.     else
  493.       [[self setSel:textPointer :textPointer] replaceSel:aString];
  494.     if (aBoolean2)
  495.       [self replaceSel:NEWLINESTRING];
  496.     [window setDocEdited:YES];
  497.     if ([[PrefAgent new] autoScroll])
  498.       [super scrollSelToVisible];
  499.     start.cp = [[markerList objectAt:([markerList count]-1)] markerPosition];
  500.     end.cp = [[markerList objectAt:([markerList count]-2)] markerPosition];
  501.     [self setSel:start.cp :end.cp];
  502.     [[markerList removeLastObject] free];
  503.     [[markerList removeLastObject] free];
  504.     [[window reenableFlushWindow] flushWindow];
  505.     return self;
  506. }
  507.  
  508. - (STR)getAll
  509. {
  510.     [super selectAll:self];
  511.     return [self getSelection];
  512. }
  513.  
  514. - (STR)getSelection
  515. {
  516.     NXSelPt start,end;
  517.     int length;
  518.  
  519.     [super getSel:&start :&end];
  520.     length = end.cp-start.cp;
  521.     selectionBuffer = (char *)(selectionBuffer==NULL?malloc(length+1):realloc(selectionBuffer, length+1));
  522.     [super getSubstring:selectionBuffer start:start.cp length:length];
  523.     selectionBuffer[length] = '\0';
  524.     if ([window isMemberOf:[InteractionWin class]]&&(*selectionBuffer))
  525.       if ([[PrefAgent new] transcriptMode])
  526.     [self setInsertionBar:textLength];
  527.       else
  528.         [self setInsertionBar:end.cp];
  529.     return selectionBuffer;
  530. }
  531.  
  532. - setInsertionBar:(int)anInt
  533. {
  534.     int textPointer = (textLength<=anInt?textLength-1:anInt);
  535.     [[markerList objectAt:0] adjustTo:textPointer];
  536.     return [super setSel:textPointer :textPointer];
  537. }
  538.  
  539. - (BOOL)selectScope
  540. {
  541.     NXSelPt start,end;
  542.     int startpos,endpos;
  543.     char textBuffer[textLength+1];
  544.  
  545.     [super getSel:&start :&end];
  546.     [super getSubstring:textBuffer start:0 length:textLength];
  547.     textBuffer[textLength] = '\0';
  548.     if (((startpos = findBeginningOfScope(self, start.cp, textBuffer))<0)||((endpos = findEndOfScope(self, startpos, textBuffer))<0)||(endpos<end.cp))
  549.       return NO;
  550.     [super setSel:startpos :endpos];
  551.     return YES;
  552. }
  553.  
  554. - (BOOL)selectScope2
  555. {
  556.     int startpos,endpos;
  557.     char textBuffer[textLength+1];
  558.  
  559.     [super getSubstring:textBuffer start:0 length:textLength];
  560.     textBuffer[textLength] = '\0';
  561.     if (((startpos = findBeginningOfScope(self, last1ClickPos, textBuffer))<0)||((endpos = findEndOfScope(self, startpos, textBuffer))<0)||(endpos<last1ClickPos))
  562.       return NO;
  563.     [super setSel:startpos :endpos];
  564.     return YES;
  565. }
  566.  
  567. - saveSelectionState
  568. {
  569.     NXSelPt start,end;
  570.     [super getSel:&start :&end];
  571.     [markerList addObject:[[Marker alloc] initAt:end.cp type:MARKCENTER]];
  572.     [markerList addObject:[[Marker alloc] initAt:start.cp type:MARKCENTER]];
  573.     return self;
  574. }
  575.  
  576. - restoreSelectionState
  577. {
  578.     int start,end;
  579.     if ([markerList count]<3) return nil;
  580.     start = [[markerList objectAt:([markerList count]-1)] markerPosition];
  581.     end = [[markerList objectAt:([markerList count]-2)] markerPosition];
  582.     [self setSel:start :end];
  583.     [[markerList removeLastObject] free];
  584.     [[markerList removeLastObject] free];
  585.     return self;
  586. }
  587.  
  588. - (int)stringSearch:(const char *)theTarget forAgent:theAgent direction:(int)direction ignoreCase:(BOOL)ignorecase  wholeWord:(BOOL)wholeword inSel:(BOOL)inSel
  589. {
  590.     NXSelPt start,end;
  591.     int slength,tlength = strlen(theTarget);
  592.     int startcp,endcp,i,origTextLength=textLength;
  593.     char *buffer;
  594.  
  595.     [super getSel:&start :&end];
  596.     slength = end.cp-start.cp;
  597.     if (inSel)
  598.       {
  599.         buffer = (char *)NXZoneMalloc([theAgent zone], slength+1);
  600.         [super getSubstring:buffer start:start.cp length:slength];
  601.         startcp = 0;
  602.         endcp = (slength-tlength);
  603.       }
  604.     else
  605.       {
  606.         buffer = (char *)NXZoneMalloc([theAgent zone], origTextLength+1);
  607.         [super getSubstring:buffer start:0 length:origTextLength];
  608.         startcp = (direction==FINDFORWARD?end.cp:start.cp-tlength);
  609.         endcp = (direction==FINDFORWARD?origTextLength-tlength+1:0);
  610.       }
  611.     for(i=startcp; (direction==FINDFORWARD?i<=endcp:i>=endcp); i+=direction)
  612.       if (!NXOrderStrings((unsigned char *)theTarget,(unsigned char *)(buffer+i),!ignorecase,tlength,NULL))
  613.         if (!(wholeword&&(((i>0)&&NXIsAlNum(buffer[i-1]))||(((i+tlength)<=(inSel?slength:origTextLength))&&NXIsAlNum(buffer[i+tlength])))))
  614.           {
  615.         int offset=i+(inSel?start.cp:0)+(textLength-origTextLength);
  616.         [super setSel:offset :(offset+tlength)];
  617.         NXZoneFree([theAgent zone], buffer);
  618.         return 1;
  619.       }
  620.     if (inSel)
  621.       {
  622.         NXZoneFree([theAgent zone], buffer);
  623.         return 0;
  624.       }
  625.     startcp = (direction==FINDFORWARD?0:textLength-tlength+1);
  626.     endcp = (direction==FINDFORWARD?end.cp-2:start.cp+1);
  627.     origTextLength = textLength;
  628.     for(i=startcp; (direction==FINDFORWARD?i<=endcp:i>=endcp); i+=direction)
  629.       if (!NXOrderStrings((unsigned char *)theTarget,(unsigned char *)(buffer+i),!ignorecase,tlength,NULL))
  630.         if (!(wholeword&&(((i>0)&&NXIsAlNum(buffer[i-1]))||(((i+tlength)<=origTextLength)&&NXIsAlNum(buffer[i+tlength])))))
  631.           {
  632.             int offset=i+(textLength-origTextLength);
  633.             [super setSel:offset :(offset+tlength)];
  634.         NXZoneFree([theAgent zone], buffer);
  635.         return 1;
  636.           }
  637.     NXZoneFree([theAgent zone], buffer);
  638.     return 0;
  639. }
  640.  
  641. - formatTextSelectionOnly:(BOOL)selOnly
  642. {
  643.   NXSelPt start, end;
  644.   int startline, finishline, i;
  645.   
  646.   [self getSel:&start :&end];
  647.   [markerList addObject:[[Marker alloc] initAt:end.cp type:MARKRIGHT]];
  648.   [markerList addObject:[[Marker alloc] initAt:start.cp type:MARKRIGHT]];
  649.  
  650.   if (!selOnly && start.cp >= end.cp){
  651.     startline = 1;
  652.     finishline = [self lineFromPosition:textLength];
  653.   } else {
  654.     char c;
  655.     startline = [self lineFromPosition:start.cp];
  656.     [self getSubstring:&c start:end.cp-1 length:1];
  657.     finishline = [self lineFromPosition: ((c == '\n') && (start.cp < end.cp)
  658.                                           ? (end.cp - 1)
  659.                                           : end.cp)];
  660.   }
  661.  
  662.   for (i = startline; i <= finishline; i++) {
  663.     int desired = [indentationState determineIndentation:i];
  664.  
  665.     if (desired >= 0){
  666.       BOOL anyTabs = NO;
  667.       int current;
  668.       unsigned char c;
  669.       NXStream *stream = [self stream];
  670.  
  671.       NXSeek(stream, [self positionFromLine:i], NX_FROMSTART);
  672.       for (current = 0; ; current++){
  673.         if((c = NXGetc(stream)) == '\t')
  674.           anyTabs = YES;
  675.         else if (c != ' ')
  676.           break;
  677.       }
  678. #ifdef DEBUG_INDENTATION
  679.       fprintf(stderr, "Linen %d; current = %d, desired = %d%s\n",
  680.               i, current, desired, anyTabs?"; tabs present":"");
  681. #endif
  682.       if(anyTabs || current != desired){
  683.         int startpos = [self positionFromLine:i];
  684.         char spacestring[desired+1];
  685.  
  686.         [self setSel:startpos :startpos+current];
  687.         memset(spacestring, ' ', desired);
  688.         spacestring[desired] = '\0';
  689.         [self replaceSel:spacestring];
  690.         [window setDocEdited:YES];
  691.       }
  692.       else
  693.         NXUngetc(stream);
  694.     }
  695.   }
  696.   start.cp = [[markerList objectAt:([markerList count]-1)] markerPosition];
  697.   end.cp = [[markerList objectAt:([markerList count]-2)] markerPosition];
  698.   [self setSel:start.cp :end.cp];
  699.   [[markerList removeLastObject] free];
  700.   [[markerList removeLastObject] free];
  701.   return self;
  702. }
  703.  
  704. @end
  705.  
  706. static BOOL inSet(const char element, const char *set)
  707. {
  708.     int i;
  709.  
  710.     for(i=0; *(set+i); i++)
  711.       if (element==*(set+i))
  712.         return YES;
  713.     return NO;
  714. }
  715.  
  716. static int findParensMatch(id self, const char parens, int startpos, char *text)
  717. {
  718.     int i,count=0,init,match,direction,textlength=[self textLength];
  719.  
  720.     switch (init=parens)
  721.       {
  722.         case LEFT_PAREN   : match = RIGHT_PAREN; direction = 1; break;
  723.         case LEFT_BRACKET : match = RIGHT_BRACKET; direction = 1; break;
  724.         case LEFT_BRACE   : match = RIGHT_BRACE; direction = 1; break;
  725.         case RIGHT_PAREN  : match = LEFT_PAREN; direction = -1; break;
  726.         case RIGHT_BRACKET: match = LEFT_BRACKET; direction = -1; break;
  727.         case RIGHT_BRACE  : match = LEFT_BRACE; direction = -1; break;
  728.         default : return NOT_REALLY_PAREN;
  729.       }
  730.     if ((text[startpos-2]=='#')&&(text[startpos-1]=='\\'))
  731.       return NOT_REALLY_PAREN;
  732.     for(i=startpos+direction; ((i>=0)&&(i<textlength))&&((count>0)||(text[i]!=match)||((i>=2)&&(text[i-2]=='#')&&(text[i-1]=='\\'))); i+=direction)
  733.       {
  734.         if ((direction>0)&&(text[i]=='\"')&&!((text[i-2]=='#')&&(text[i-1]=='\\')))
  735.           {
  736.         i=skipstring(text,textlength,i);
  737.         if (i<0) break;
  738.       }
  739.         else if ((direction>0)&&(text[i]==COMMENTCHAR)&&!((text[i-2]=='#')&&(text[i-1]=='\\')))
  740.           i=skipspacecommentnewline(text,textlength,i-1)-1;
  741.         else if ((text[i]==init)&&!((text[i-2]=='#')&&(text[i-1]=='\\')))
  742.           count++;
  743.         else if ((text[i]==match)&&!((text[i-2]=='#')&&(text[i-1]=='\\')))
  744.           count--;
  745.       }
  746.     return ((i>=textlength)||(i<0)?PAREN_NO_MATCH:i);
  747. }
  748.  
  749. static int findBeginningOfScope(id self, int currpos, char *text)
  750. {
  751.     int line,startpos;
  752.  
  753.     line = [self lineFromPosition:currpos];
  754.     startpos = [self positionFromLine:line];
  755.     for(; line&&(!text[startpos]||inSet(text[startpos],BADLINEBEGINSET)); startpos = [self positionFromLine:--line]);
  756.     return (line<=0?-1:startpos);
  757. }
  758.  
  759. static int skipspacecommentnewline(const char buffer[], int textlength, int endpos)
  760. {
  761.     for(endpos++; (endpos<textlength)&&(buffer[endpos]!=NEWLINECHAR)&&NXIsSpace(buffer[endpos]); endpos++);
  762.       if (buffer[endpos]==NEWLINECHAR)
  763.     return endpos+1;
  764.       else if ((buffer[endpos]==COMMENTCHAR)&&!((endpos>=2)&&(buffer[endpos-2]=='#')&&(buffer[endpos-1]=='\\')))
  765.     {
  766.       for(endpos++; (endpos<textlength)&&(buffer[endpos]!=NEWLINECHAR); endpos++);
  767.       if (endpos>=textlength) return endpos;
  768.       return endpos+1;
  769.     }
  770.       else
  771.     return endpos;
  772. }
  773.  
  774. static int skipstring(const char buffer[], int textlength, int endpos)
  775. {
  776.     if ((endpos>=2)&&(buffer[endpos-1]=='\\')&&(buffer[endpos-2]=='#'))
  777.       return endpos;
  778.     while (1)
  779.       {
  780.         for(endpos++; (endpos<textlength)&&(buffer[endpos]!='\"'); endpos++);
  781.         if (endpos>=textlength) return -1;
  782.         if ((buffer[endpos-1]!='\\')||((endpos>=2)&&(buffer[endpos-2]=='\\'))) return endpos;
  783.       }
  784. }
  785.  
  786. static int findEndOfScope(id self, int startpos, char *text)
  787. {
  788.     int endpos=0;
  789.  
  790.     for(; text[startpos]&&inSet(text[startpos],PREFIXCHARS); startpos++);
  791.     if ((text[startpos]=='\\')&&(text[startpos-1]=='#'))
  792.       startpos--;
  793.     if (inSet(text[startpos],LEFT_PARENS))
  794.       {
  795.     if ((endpos = findParensMatch(self,text[startpos],startpos,text))<0)
  796.       return -1;
  797.       }
  798.     else if (text[startpos]=='\"')
  799.       {
  800.     if ((endpos = skipstring(text,strlen(text),startpos))<0)
  801.       return -1;
  802.       }
  803.     else
  804.       {
  805.         for(endpos=startpos; text[endpos]&&(!inSet(text[endpos],DELIMETERSET)||((text[endpos-2]=='#')&&(text[endpos-1]=='\\'))); endpos++);
  806.         endpos--;
  807.       }
  808.     endpos = skipspacecommentnewline(text, strlen(text), endpos);
  809.     if (endpos<=startpos) return -1;
  810.     return endpos;
  811. }
  812.  
  813. static int getcolumnofposition(id self, int charpos)
  814. {
  815.     return charpos-[self positionFromLine:[self lineFromPosition:charpos]];
  816. }
  817.  
  818.  
  819. @implementation Marker
  820.  
  821. - init
  822. {
  823.     return [self initAt:0 type:MARKCENTER];
  824. }
  825.  
  826. - initAt:(int)anInt type:(signed char)aChar
  827. {
  828.     [super init];
  829.     charpos = anInt;
  830.     type = aChar;
  831.     return self;
  832. }
  833.  
  834. - (int)markerPosition
  835. {
  836.     return charpos;
  837. }
  838.  
  839. - adjustTo:(int)anInt forPositionsFrom:(int)begin to:(int)end andAfterBy:(int)anInt2
  840. {
  841.     if (((charpos==begin)&&(type==MARKRIGHT))||((begin<charpos)&&(charpos<end))||((charpos==end)&&(type==MARKLEFT)))
  842.       charpos = anInt;
  843.     else if (end<=charpos)
  844.       charpos += anInt2;
  845.     return self;
  846. }
  847.  
  848. - adjustTo:(int)anInt
  849. {
  850.     charpos = anInt;
  851.     return self;
  852. }
  853.  
  854. @end
  855.  
  856. @implementation IndentationState
  857.  
  858. - changeAt:(int)position
  859. {
  860.   stream = NULL;
  861.   if ( [text lineFromPosition:position] < knownLine ){
  862.     knownLine = 1;
  863.     while(contexts){
  864.       Context *temp = contexts;
  865.       contexts = temp->next;
  866.       free(temp);
  867.     }
  868.     inString = NO;
  869.   }
  870.   return self;
  871. }
  872.  
  873. - init
  874. {
  875.     return [self initText:nil];
  876. }
  877.  
  878. - initText:theText
  879. {
  880.   [super init];
  881.   text = theText;
  882.   knownLine = 1;
  883.   stream = NULL;
  884.   contexts = NULL;
  885.   inString = NO;
  886.   keywordListVersion = [[PrefAgent new] keywordListVersion];
  887.   return self;
  888. }
  889.  
  890. - free
  891. {
  892.     while(contexts)
  893.       {
  894.         Context *temp = contexts;
  895.         contexts = temp->next;
  896.         free(temp);
  897.       }
  898.     return [super free];
  899. }
  900.  
  901. - (int)determineIndentation:(int)line
  902. {
  903.   int currentKeywordListVersion = [[PrefAgent new] keywordListVersion];
  904.  
  905.   if (keywordListVersion != currentKeywordListVersion){
  906.     keywordListVersion = currentKeywordListVersion;
  907.     [self changeAt:0];
  908.   }
  909.   if (line < knownLine || (line - knownLine) > MAX_FWD_ONLY){
  910.     int l;
  911.     for(l = line-1; l > 1 && l != knownLine; l--){
  912.       char c;
  913.       [text getSubstring:&c start:[text positionFromLine:l] length:1];
  914.       if(!index(BADLINEBEGINSET, c))
  915.         break;
  916.     }
  917.     if(l == 0)
  918.       l++;
  919.     if(l != knownLine){
  920.       knownLine = l;
  921.       stream = NULL;
  922.       inString = NO;
  923.       while(contexts){
  924.         Context *temp = contexts;
  925.         contexts = temp->next;
  926.         free(temp);
  927.       }
  928.     }
  929.   }
  930.   while(knownLine < line)
  931.     [self scanLine];
  932.   return [self indent];
  933. }
  934.  
  935. - setupStream
  936. {
  937.   if (stream == NULL){
  938.     stream = [text stream];
  939.     NXSeek(stream, [text positionFromLine:knownLine], NX_FROMSTART);
  940.   }
  941.   return self;
  942. }
  943.  
  944. - (int)indent
  945. {
  946. #ifdef DEBUG_INDENTATION
  947.   fprintf(stderr, "start of IndentationState/indent ");
  948.   [self describeSelf];
  949. #endif
  950.   if (inString)
  951.     return -1;
  952.   else if (contexts == NULL)
  953.     return 0;
  954.   else {
  955.     Context *c = contexts;
  956.     if (c->elts < c->distinguishedElts + 1)
  957.       return BIG_INDENT + getcolumnofposition(text, c->startPosition);
  958.     else if (c->elts == c->distinguishedElts + 1)
  959.       return SMALL_INDENT + getcolumnofposition(text, c->startPosition);
  960.     else
  961.       return getcolumnofposition(text, c->alignmentPosition);
  962.   }
  963. }
  964.  
  965. - scanLine
  966. {
  967.   BOOL anythingYet = NO;
  968.   Context *context = contexts;
  969.   BOOL prefix = NO;
  970.   char c;
  971.  
  972. #ifdef DEBUG_INDENTATION
  973.   fprintf(stderr, "start of IndentationState/scanLine ");
  974.   [self describeSelf];
  975. #endif
  976.   [self setupStream];
  977.   knownLine++;
  978.   if (inString)
  979.     goto scan_string;
  980.   while (1){
  981.     switch (c = NXGetc(stream)){
  982.     case '\n': case '\377':
  983.       return self;
  984.     case ' ': case '\t':
  985.       continue;
  986.     case ';':
  987.       while(NXGetc(stream) != '\n')
  988.         ;
  989.       return self;
  990.     case '\'': case'`': case ',': case '@': case '#':
  991.       if(!prefix){
  992.         prefix = YES;
  993.         newElt(context, anythingYet, stream);
  994.       }
  995.       if(c == '#'){
  996.         if(NXGetc(stream) == '\\'){
  997.           if(isalpha(c = NXGetc(stream))){
  998.             while(index(DELIMETERSET, NXGetc(stream)) == NULL)
  999.               ;
  1000.             NXUngetc(stream);
  1001.             break;
  1002.           } else if(c == '\n')
  1003.             NXUngetc(stream);
  1004.           else
  1005.             break;
  1006.         } else
  1007.           NXUngetc(stream);
  1008.       }
  1009.       continue;
  1010.     case '(': case '[': case '{':
  1011.       if(!prefix)
  1012.         newElt(context, anythingYet, stream);
  1013.       contexts = malloc(sizeof(Context));
  1014.       ((Context *)contexts)->next = context;
  1015.       context = contexts;
  1016.       context->isLet = NO;
  1017.       context->elts = 0;
  1018.       context->distinguishedElts = -1;
  1019.       context->startPosition = (context->alignmentPosition=NXTell(stream)) - 1;
  1020.       break;
  1021.     case ')': case ']': case '}':
  1022.       if(context != NULL){
  1023.         contexts = context->next;
  1024.         free(context);
  1025.         context = contexts;
  1026.       }
  1027.       break;
  1028.     case '"':
  1029.       inString = YES;
  1030.       if(!prefix)
  1031.         newElt(context, anythingYet, stream);
  1032.     scan_string:
  1033.       while (1){
  1034.         if ((c = NXGetc(stream)) == '\n')
  1035.           return self;
  1036.         else if (c == '\\'){
  1037.           if ((c = NXGetc(stream)) == '\n')
  1038.             NXUngetc(stream);
  1039.         } else if (c == '"'){
  1040.           inString = NO;
  1041.           break;
  1042.         }
  1043.       }
  1044.       break;
  1045.     default:
  1046. #ifdef DEBUG_INDENTATION
  1047.       fprintf(stderr, "start of token ");
  1048.       [self describeSelf];
  1049. #endif
  1050.       if(!prefix)
  1051.         newElt(context, anythingYet, stream);
  1052.       if(!prefix && context != NULL && context->elts == 2
  1053.          && context->isLet)
  1054.         context->distinguishedElts++;
  1055.       if (!prefix && context != NULL && context->elts == 1) {
  1056.         char tokbuf[MAXTOK+1];
  1057.         int i = 0;
  1058.         do {
  1059.           if (i < MAXTOK)
  1060.             tokbuf[i++] = c;
  1061.         } while(index(DELIMETERSET, c = NXGetc(stream)) == NULL);
  1062.         NXUngetc(stream);
  1063.         tokbuf[i] = '\0';
  1064.         context->distinguishedElts = [[PrefAgent new] deForSpecialForm:tokbuf];
  1065.         if(!NXOrderStrings((unsigned char *) tokbuf, (unsigned char *) "let",
  1066.                            NO, -1, NULL))
  1067.           context->isLet = YES;
  1068.       } else{
  1069.         while(index(DELIMETERSET, NXGetc(stream)) == NULL)
  1070.           ;
  1071.         NXUngetc(stream);
  1072.       }
  1073. #ifdef DEBUG_INDENTATION
  1074.       fprintf(stderr, "end of token ");
  1075.       [self describeSelf];
  1076. #endif
  1077.       break;
  1078.     }
  1079.     prefix = NO;
  1080.     anythingYet = YES;
  1081.   }
  1082. }
  1083.  
  1084. - describeSelf
  1085. {
  1086.   Context *context = contexts;
  1087.   fprintf(stderr, "indentation state\n");
  1088.   fprintf(stderr, "  knownLine: %d\n", knownLine);
  1089.   if(stream == NULL)
  1090.     fprintf(stderr, "  no stream\n");
  1091.   else{
  1092.     int pos = NXTell(stream);
  1093.     fprintf(stderr, "  stream position: %d (row %d, col %d)\n",
  1094.             pos, [text lineFromPosition:pos],
  1095.             getcolumnofposition(text, pos));
  1096.   }
  1097.   fprintf(stderr, "  %sin string\n", inString?"":"not ");
  1098.   while(context){
  1099.     fprintf(stderr, "  elts: %d, des: %d, startpos: %d, alignpos: %d\n",
  1100.             context->elts, context->distinguishedElts, context->startPosition,
  1101.             context->alignmentPosition);
  1102.     context = context->next;
  1103.   }
  1104.   return self;
  1105. }
  1106.  
  1107. @end
  1108.  
  1109. static void newElt(Context *context, BOOL anythingYet, NXStream *stream){
  1110.   if (context == NULL)
  1111.     return;
  1112.   if(context->elts == 1 || (context->elts > 1 && !anythingYet))
  1113.     context->alignmentPosition = NXTell(stream)-1;
  1114.   context->elts++;
  1115. }
  1116.